package com.luo.d3s.light.jpa.ec.application.service.impl;

import com.luo.d3s.core.application.dto.Response;
import com.luo.d3s.core.application.dto.SingleResponse;
import com.luo.d3s.core.util.validation.Validates;
import com.luo.d3s.light.jpa.ec.application.assembler.GoodsAssembler;
import com.luo.d3s.light.jpa.ec.application.dto.command.GoodsCreateCommand;
import com.luo.d3s.light.jpa.ec.application.dto.command.GoodsModifyCommand;
import com.luo.d3s.light.jpa.ec.application.dto.vo.GoodsVo;
import com.luo.d3s.light.jpa.ec.application.service.GoodsCommandService;
import com.luo.d3s.light.jpa.ec.domain.base.BizId;
import com.luo.d3s.light.jpa.ec.domain.goods.*;
import com.luo.d3s.light.jpa.ec.domain.specification.CategoryExistSpecification;
import com.luo.d3s.light.jpa.ec.domain.specification.GoodsShelvedNotExistSpecification;
import com.luo.d3s.light.jpa.ec.domain.specification.GoodsTagExistSpecification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;

/**
 * 商品命令处理服务实现类
 *
 * @author luohq
 * @date 2023-01-04 17:06
 */
@Service
public class GoodsCommandServiceImpl implements GoodsCommandService {

    private GoodsRepository goodsRepository;

    private CategoryExistSpecification categoryExistSpecification;
    private GoodsShelvedNotExistSpecification goodsShelvedNotExistSpecification;
    private GoodsTagExistSpecification goodsTagExistSpecification;

    public GoodsCommandServiceImpl(GoodsRepository goodsRepository,
                                   CategoryExistSpecification categoryExistSpecification,
                                   GoodsShelvedNotExistSpecification goodsShelvedNotExistSpecification,
                                   GoodsTagExistSpecification goodsTagExistSpecification) {
        this.goodsRepository = goodsRepository;
        this.categoryExistSpecification = categoryExistSpecification;
        this.goodsShelvedNotExistSpecification = goodsShelvedNotExistSpecification;
        this.goodsTagExistSpecification = goodsTagExistSpecification;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public SingleResponse<GoodsVo> createGoods(GoodsCreateCommand goodsCreateCommand) {
        //创建商品实体
        Goods goods = GoodsFactory.createGoods(goodsCreateCommand);
        //验证新增商品分类是否存在
        this.categoryExistSpecification.isSatisfiedBy(goods.getCategoryId());
        //验证商品标签是否存在
        this.goodsTagExistSpecification.isSatisfiedBy(goods.getTagIds());
        //保存商品
        goods = this.goodsRepository.save(goods);
        //转换GoodsDto
        return SingleResponse.of(GoodsAssembler.toGoodsVo(goods));
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Response modifyGoods(GoodsModifyCommand goodsModifyCommand) {
        //查询商品实体
        Goods goods = this.goodsRepository.findById(BizId.fromValue(goodsModifyCommand.getId())).orElse(null);
        Validates.notNull(goods, "goods does not exist");
        //修改基础信息
        goods.modifyBasicInfo(goodsModifyCommand);
        //验证待修改商品分类是否存在
        this.categoryExistSpecification.isSatisfiedBy(goods.getCategoryId());
        //验证商品标签是否存在
        this.goodsTagExistSpecification.isSatisfiedBy(goods.getTagIds());
        //保存商品
        goods = this.goodsRepository.save(goods);
        return Response.buildSuccess();
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Response removeGoods(List<Long> goodsIds) {
        //转换商品ID
        Set<BizId> goodsEntityIds = BizId.fromValues(goodsIds);
        //验证是不存在已上架状态的商品才可批量删除
        this.goodsShelvedNotExistSpecification.isSatisfiedBy(goodsEntityIds);
        /*
          批量删除商品
          注：goodsRepository.deleteAllByIdInBatch不会删除关联的goods_tag_binding，但是批量删除主表delete .. where id in (...)，
             调整为deleteAllById或deleteById，会先selectById，后续会先删除关联的goods_tag_binding，然后再删除主表
         */
        this.goodsRepository.deleteAllById(goodsEntityIds);
        return Response.buildSuccess();
    }

    /**
     * 上架商品 - 事务脚本实现方式
     *
     * @param goodsIds 商品ID列表
     * @return 响应结果
     */
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Response shelveGoods(List<Long> goodsIds) {
        //批量更新商品状态为已上架
        this.goodsRepository.batchModifyGoodsStatus(BizId.fromValues(goodsIds), GoodsStatus.SHELVED);
        return Response.buildSuccess();
    }

    /**
     * 下架商品 - 事务脚本实现方式
     *
     * @param goodsIds 商品ID列表
     * @return 响应结果
     */
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Response unshelveGoods(List<Long> goodsIds) {
        //批量更新商品状态为已下架
        this.goodsRepository.batchModifyGoodsStatus(BizId.fromValues(goodsIds), GoodsStatus.UNSHELVED);
        return Response.buildSuccess();
    }


    /**
     * 上架商品 - DDD实现方式<br/>
     * 注：由于多次查询、更新数据库，出于性能考虑，暂未使用该实现方式。
     *
     * @param goodsIds 商品ID列表
     * @return 响应结果
     */
    @Deprecated
    @Transactional(rollbackFor = Throwable.class)
    public Response shelveGoodsDDD(List<Long> goodsIds) {
        //依次遍历商品ID并设置上架状态
        BizId.fromValues(goodsIds)
                .forEach(goodsId -> {
                    //查询商品实体
                    Goods goods = this.goodsRepository.findById(goodsId).orElse(null);
                    Validates.notNull(goods, String.format("goods %s does not exist", goods));
                    //设置商品上架
                    goods.shelve();
                    //保存商品信息
                    this.goodsRepository.save(goods);
                });
        return Response.buildSuccess();
    }

    /**
     * 下架商品 - DDD实现方式<br/>
     * 注：由于多次查询、更新数据库，出于性能考虑，暂未使用该实现方式。
     *
     * @param goodsIds 商品ID列表
     * @return 响应结果
     */
    @Deprecated
    @Transactional(rollbackFor = Throwable.class)
    public Response unshelveGoodsDDD(List<Long> goodsIds) {
        //依次遍历商品ID并设置上架状态
        BizId.fromValues(goodsIds)
                .forEach(goodsId -> {
                    //查询商品实体
                    Goods goods = this.goodsRepository.findById(goodsId).orElse(null);
                    Validates.notNull(goods, String.format("goods %s does not exist", goods));
                    //设置商品下架
                    goods.unshelve();
                    //保存商品信息
                    this.goodsRepository.save(goods);
                });
        return Response.buildSuccess();
    }

}
